import type { TypingRegisterType, enterItemsType, expressionItemsType, Bracket_OperatorType, TryPush_PopResultType } from "./types";
import type { BracketLeftType, BracketRightType } from "@base/Bracket/types";
import type { OperatorType } from "@base/Operator/types";
import { enterItemsEnum } from "./enum";
import { isBracket, isOperator } from "./isInstanceOf";

// 调用实例tryPuse返回的结果，避免频繁cg
const tryPuseResultDone: TryPush_PopResultType = {
    isDone: true,
}
const tryPuseResultFail: TryPush_PopResultType = {
    isDone: false,
    msg: '不得输入超过12位（含小数点）'
}

export class TypingRegister implements TypingRegisterType {
    /**
     * 每次键入，最多可容纳的长度
     */
    #maxLength:number;
    /**
     * @desc 输入栈：每次输入的式子中的基本单元，例如加、减、3.14
     * @kind 如果输入一个运算符、括号，则栈中只有它一个元素，如[Add]
     * @kind 如果输入数字，会把输入的单个数字按输入顺序存在栈中，如3.14 -> ['3', '.', '1', '4']
     * 
     */
    #stack: Array<enterItemsType> = [];
    /**
     * @desc 对应输入栈已存基本单元的类型
     * @kind initial：0 初始状态，未输入过元素，或被重置
     * @kind operator_bracket：1 括号或运算符
     * @kind float：2 小数
     * @kind int：3 整数
     */
    #currentItemType: enterItemsEnum = enterItemsEnum.initial;
    constructor(stackMaxLength:number) {
        this.#maxLength = stackMaxLength
    }
    /**
     * 存入运算符、括号，并更新类型
     * @param targetItem 正在点击输入的运算符、括号，对应的实例元素
     */
    #formatOperator(targetItem: BracketLeftType | BracketRightType | OperatorType): void {
        this.#stack.length = 0;
        this.#stack.push(targetItem);
        this.#currentItemType = enterItemsEnum.operator_bracket;
    }
    /**
     * 存入正在点击输入的数字（包括小数点），并更新类型
     * @param item 正在点击输入的数字（包括小数点）对应的字符串
     */
    #formatNum(item: string): void {
        // 如果已经暂存了非num类型，则先清空栈。枚举类中，非数值的枚举值小于float的枚举值
        if (this.#currentItemType < enterItemsEnum.float) {
            this.#initial();
        }

        // 如果输入的是小数点
        if (item === '.') {
            // 当前栈中，存档的是小数，忽视此次输入的小数点
            if (this.#currentItemType === enterItemsEnum.float) {
                return;
            }

            // 自动补零
            if (this.#stack.length === 0) {
                this.#stack.push('0');
            }
            // 把小数点存入栈中，并把当前栈存储类型更新为小数
            this.#stack.push(item);
            this.#currentItemType = enterItemsEnum.float;
            return;
        }

        // 输入的是数字
        this.#stack.push(item);
        if (this.#currentItemType === enterItemsEnum.float) {
            // 如果是float，说明输入过小数点，现在是输入小数位
            return;
        }
        this.#currentItemType = enterItemsEnum.int;
        return;
    }
    /**
     * 清空栈并更新类型
     */
    #initial() {
        this.#stack.length = 0;
        this.#currentItemType = enterItemsEnum.initial
    }
    /**
     * 尝试往栈中输入元素
     * @param enterItem 尝试输入的元素
     * @returns 输入结果：{isDone: boolean, msg: string}
     */
    tryPush(enterItem: enterItemsType): TryPush_PopResultType {
        

        // 输入的是括号、操作符
        if (isBracket(enterItem) || isOperator(enterItem)) {
            this.#formatOperator(enterItem as Bracket_OperatorType);
            return tryPuseResultDone;
        }
        // 栈元素个数，限制数值输入长于在设置范围内
        if (this.#stack.length > this.#maxLength) {
            return tryPuseResultFail;
        }
        // 输入的是数字、小数点
        this.#formatNum(enterItem as string);
        return tryPuseResultDone;
    }
    /**
     * pop最后一个元素，并更新类型
     */
    tryPop(): void {
        if (this.#stack.length === 0) {
            return;
        }
        if (this.#stack.length === 1) {
            // 将要删除的是最后一个元素，清空栈，返回true
            this.clear();
            return;
        }
        if (this.#stack.pop() === '.') {
            // 如果将pop的是小数点，修改typeIndex为3(int)
            this.#currentItemType = enterItemsEnum.int;
            return;
        }
    }
    /**
     * 将中缀表达式中的元素重新输入到栈中
     * @param packet 中缀表达式中的元素
     */
    unPacket(packet: expressionItemsType): void | never {
        this.clear();
        // 操作符、括号
        if (isBracket(packet) || isOperator(packet)) {
            this.tryPush(packet as Bracket_OperatorType);
            return;
        };
        // 操作数
        if (typeof (packet) === 'number' && Number.isFinite(packet)) {
            // 如果是数字，先转为字符数组，再依次存入
            let tempArr = packet.toString().split('');
            tempArr.length = Math.min(tempArr.length, this.#maxLength);
            for (let i = 0; i < tempArr.length; i++) {
                this.tryPush(tempArr[i]);
            }
            return;
        };

        throw new Error("unPackage解析错误");
    }
    /**
     * 重置当前存储的栈内容
     */
    clear(): void {
        this.#initial();
    }
    /**
     * 判断当前是否栈空
     * @returns 是否栈空
     */
    isEmpty(): boolean {
        return this.#stack.length === 0;
    }
    /**
     * 获取正在输入（栈）的内容，并打包为中缀表达式中的一个元素
     * @returns 中缀表达式中的元素
     */
    getPacket(): expressionItemsType | undefined {
        if (this.#currentItemType === enterItemsEnum.operator_bracket) {
            return this.#stack[0] as Bracket_OperatorType;
        }
        if (this.#currentItemType >= enterItemsEnum.float) {
            // 转为float类型，如果字符串末尾有小数点，头部有0，则被去掉
            return parseFloat(this.#stack.join('')) || 0;
        }
        if (this.#currentItemType === enterItemsEnum.initial) {
            return undefined;
        }
    }
    /**
     * 获取正在输入的栈元素
     * @returns 正在输入的栈元素
     */
    getItems(): enterItemsType[] {
        return this.#stack;
    }
    /**
     * 获取当前栈中的内容类型
     * @returns 当前栈中的内容类型枚举值
     */
    getCurrentType(): enterItemsEnum {
        return this.#currentItemType;
    }

}